//
//  CycleScrollView.swift
//  CycleScrollView
//
//  Created by Ben on 2021/9/18.
//

import SwiftUI

class CycleScrollDataSource<Content: Codable>: Identifiable, Codable, Hashable {
    static func == (lhs: CycleScrollDataSource, rhs: CycleScrollDataSource) -> Bool {
        return lhs.id == rhs.id
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    var id = UUID().uuidString
    var content: Content
    
    init(content: Content) {
        self.content = content
    }
}

struct CycleScrollView<T: Codable, CycleItem: View>: View {
    
    init(scrollDirection: Axis.Set, showsIndicators: Bool, dataSource: [T], itemLength: CGFloat, @ViewBuilder item: @escaping (T) -> CycleItem) {
        self.scrollDirection = scrollDirection
        self.showsIndicators = showsIndicators
        
        self.dataSource = dataSource.map { CycleScrollDataSource(content: $0) }
        self.item = item
        self.itemLength = itemLength
    }
    
    // 滚动方向
    private var scrollDirection: Axis.Set
    // 是否显示滚动条
    private var showsIndicators: Bool
    // 要滚动显示的列表
    private var dataSource: [CycleScrollDataSource<T>]
    // 当前滚动下标
    @State private var currIndex: Int = 1
    // 每次滚动显示的view
    private var item: (T) -> CycleItem
    private var itemLength: CGFloat
    
    // 组成轮播的数据源
    private var loopDataSource: [CycleScrollDataSource<T>] {
        return [dataSource.last!] + dataSource + [dataSource.first!]
    }
    
    private let timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()

    
    var body: some View {
        
        let currentOffset = CGFloat(currIndex) * itemLength
        GeometryReader { proxy in
            
            if scrollDirection == .vertical {
                VStack(alignment: .leading, spacing: 0) {
                    ForEach(loopDataSource, id: \.self) { data in
                        item(data.content)
                            .frame(height: itemLength)
                    }
                }
                .frame(height: itemLength, alignment: .top)
                .offset(x: 0, y: -currentOffset)
                .clipped()
            } else {
                
                HStack(alignment: .center, spacing: 0) {
                    ForEach(loopDataSource, id: \.self) { data in
                        item(data.content)
                            .frame(width: itemLength)
                    }
                }
                .frame(width: itemLength, alignment: .leading)
                .offset(x: -currentOffset, y: 0)
                .clipped()
                
            }
            
        }
        .onReceive(timer) { _ in
            withAnimation(.spring()) {
                if currIndex == loopDataSource.count - 1 {
                    currIndex = 0
                } else {
                    currIndex += 1
                }
            }
        }
        .onChange(of: currIndex) { newValue in

            if newValue == 0 {
                currIndex = loopDataSource.count - 2

            } else if newValue == loopDataSource.count - 1 {
                currIndex = 1

            }
        }
    }
}
